import { Component, OnInit, Input, Output, EventEmitter, ContentChild, TemplateRef, SimpleChanges, OnChanges, DoCheck } from '@angular/core';

import { BaseView } from '../../../core/view/base-view';
import { isObject, isDate, isNumber } from '../../../util';
import { NzIconConfig } from '../../keys';

@Component({
  selector: 'bz-form-item',
  template: '',
  exportAs: 'bzFormItem'
})
export class FormItemComponent<U, B, S> extends BaseView<U, B, S> implements OnInit, OnChanges, DoCheck {

  /**
   * input 输入框 textarea 多行文本框 date 日期 date-range 日期范围 select 下拉选择框
   *  cascader 级联操作 switch 开关 modal-search 弹出查询 input-range 区间输入
   */
  @Input() bzType: 'text' | 'textarea' | 'date' | 'date-range' | 'time' | 'select' | 'cascader' | 'switch'
    | 'modal-search' | 'input-range' | 'tree-select' | 'radio' | 'checkbox' | 'custom' | 'password' = 'text';
  @Input() bzPattern: 'email' | 'url' | 'number' | 'tel' | 'mobile';
  /** 表单描述 */
  @Input() bzLabel: string;
  /** 需要双向绑定的变量 */
  @Input() bzValue: any;
  /** 表单额外显示选项 例如下拉表单，级联表单 */
  @Input() bzOptions: { label: string, value: any }[] | any;
  /** 表单是否禁用 */
  @Input() bzDisabled = false;
  /** 表单是否只读 */
  @Input() bzReadOnly = false;
  /** 表单占据的宽度 */
  @Input() bzSpan: number;
  /** 表单左侧空出来的宽度 */
  @Input() bzOffset = 0;
  /** 当前表达是否换行排列 */
  @Input() bzLineBreak = false;
  /** 表单是否必填 */
  @Input() bzRequired = false;
  /** 是否不显示 label 后面的冒号 */
  @Input() bzNoColon = true;
  /** 表单name属性 */
  @Input() bzName: string;
  /** 表单placeholder属性 */
  @Input() bzPlaceholder: string | string[] = '';
  /** 表单选项显示值 */
  @Input() bzOptionLabel = 'label';
  /** 表单选项提交值 */
  @Input() bzOptionValue = 'value';
  /** 表单是否显示，优先级高于defaultShow和customShow */
  @Input() bzShow;
  /** ant表单组件的特殊属性 */
  @Input() set bzConfig(config) {
    this._bzConfig = Object.assign({}, this.defaultConfig, config);
  }
  /** 带标签的 input，设置后置标签，可以与 nzAddOnBefore 配合使用 */
  @Input() bzAddOnBefore: string | TemplateRef<void>;
  /** 带标签的 input，设置前置标签，可以与 nzAddOnAfter 配合使用 */
  @Input() bzAddOnAfter: string | TemplateRef<void>;
  /** 带有前缀图标的 input，可以与 nzSuffix 配合使用 */
  @Input() bzSuffix: string | TemplateRef<void>;
  /** 带有后缀图标的 input，可以与 nzPrefix 配合使用 */
  @Input() bzPrefix: string | TemplateRef<void>;

  /** 内容超过最大长度时提示校验信息 */
  @Input() bzMaxLength = 500;
  @Input() maxLength = 500;
  @Input() bzMinLength: number;
  @Input() bzMax: number;
  @Input() bzMin: number;
  get bzConfig() {
    return this._bzConfig || this.defaultConfig;
  }
  @Input() bzEmptyOptionLabel: string;
  @Input() bzEmptyOptionValue = 'null';
  @Input() bzCompareWith = this._compareWithFn;
  @Input() bzLabelInfo: string;
  @Input() bzLabelIcon: NzIconConfig = {
    nzType: 'question-circle'
  };
  @Input() bzUpdateOn = 'change';
  /** 是否禁止用户通过点击更多切换表单项显示隐藏，当bzRequired为true时，禁用表单项显示隐藏 */
  @Input() bzFreezeShow = false;
  @Input() bzAsyncDefaultValue = false;

  @Output() bzValueChange = new EventEmitter();
  /** 表单项输入事件 */
  @Output() bzInput = new EventEmitter();
  @Output() bzBlur = new EventEmitter();
  @Output() bzFocus = new EventEmitter();
  @Output() bzMousedown = new EventEmitter();
  @ContentChild(TemplateRef, { static: true }) contentTemplate: TemplateRef<any>;

  lineBreakOffset = 0;
  errorType: 'email' | 'url' | 'number' | 'tel' | 'mobile' | 'required' | 'maxlength' | 'minlength' | 'max' | 'min' | '';
  touched = false;
  // 用于自动状态保存逻辑
  customShow = undefined;
  // 根据bzForm的defaultShowLineCount属性自动设定哪些表单项能够显示
  defaultShow = undefined;
  innerElement: any;
  innerInstance: any;

  private _oldBzValueForValidate;
  private _bzConfig;
  private _defaultValue: string;
  private defaultConfig = {
    maxMultipleCount: Infinity,
    dropdownMatchSelectWidth: true,
    autoSize: { minRows: 2 },
    allowClear: true,
    trim: true,
    ruleCondition: 'and',
    onOpenChange: () => { },
    onOk: () => { },
    onSearch: () => { }
  };

  constructor() {
    super();
  }

  ngOnInit() {
    this._setDefaultValue(this.bzValue);
    if (this.bzType === 'date') {
      // this.bzValue = this.bzValue || new Date();
    } else if ((this.bzType === 'checkbox' || this.bzType === 'input-range') && !Array.isArray(this.bzValue)) {
      this.bzValue = [];
    }

    if ((this.bzType === 'date' || this.bzType === 'time' || this.bzType === 'date-range') && !this.bzPlaceholder) {
      const msg = this.i18nService.getValue('@buxian');
      this.bzPlaceholder = this.bzType === 'date-range' ? [msg, msg] : msg;
    }
    if (this.bzType === 'checkbox' && !this.bzName) {
      this.loggerService.error(new Error('bzType 为 checkbox 时，bzName不能为空'), 'FormItemComponent');
    }
  }

  ngOnChanges(simpleChanges: SimpleChanges) {
    if (this.bzEmptyOptionLabel && simpleChanges.bzOptions && Array.isArray(simpleChanges.bzOptions.currentValue)) {
      simpleChanges.bzOptions.currentValue.unshift({ [this.bzOptionLabel]: this.bzEmptyOptionLabel, [this.bzOptionValue]: this.bzEmptyOptionValue });
    }
    if (this.bzAsyncDefaultValue && simpleChanges.bzValue && this._defaultValue === undefined
      && simpleChanges.bzValue.currentValue !== undefined && simpleChanges.bzValue.currentValue !== null) {
      this._setDefaultValue(simpleChanges.bzValue.currentValue);
    }
    if (this.bzType === 'checkbox' && simpleChanges.bzDisabled && Array.isArray(this.bzOptions)) {
      this.bzOptions.forEach((o) => {
        o.disabled = simpleChanges.bzDisabled.currentValue;
      });
    }
  }

  ngDoCheck(): void {
    this.validate();
  }

  validate() {
    const orginErrorType = this.errorType;
    this.errorType = '';

    if (this.bzType === 'switch' || this.bzType === 'custom' || !this.touched) {
      this.errorType = orginErrorType;
      return;
    }

    if (this._oldBzValueForValidate === this.bzValue && !!this.bzValue) {
      if (Array.isArray(this.bzValue) && (this.bzValue[0] !== this._oldBzValueForValidate[0] || this.bzValue[1] !== this._oldBzValueForValidate[1])) {
        // bzValue为数组时需要额外判断索引前两项数组成员
      } else {
        this.errorType = orginErrorType;
        return;
      }
    }

    this._oldBzValueForValidate = this.bzValue;

    if (this.bzRequired) {
      if (this.bzType === 'date-range' || this.bzType === 'cascader' || this.bzType === 'input-range') {
        if (!Array.isArray(this.bzValue) || !this.bzValue[0] || !this.bzValue[1]) {
          this.errorType = 'required';
          return;
        }
      } else if (this.isEmpty()) {
        this.errorType = 'required';
        return;
      }
    }

    if (this.bzType === 'text' || this.bzType === 'textarea') {
      if (!Number.isNaN(this.bzMaxLength) && this.bzValue && this.bzValue.length > this.bzMaxLength) {
        this.errorType = 'maxlength';
      } else if (!Number.isNaN(this.bzMinLength) && (this.bzValue || '').length < this.bzMinLength) {
        this.errorType = 'minlength';
      } else if (this.bzPattern === 'number' && this.bzValue && !Number.isNaN(this.bzMax) && parseInt(this.bzValue, 10) > this.bzMax) {
        this.errorType = 'max';
      } else if (this.bzPattern === 'number' && this.bzValue && !Number.isNaN(this.bzMin) && parseInt(this.bzValue, 10) < this.bzMin) {
        this.errorType = 'max';
      } else if (this.bzPattern === 'email' && this.bzValue && !this.emailRegExp.test(this.bzValue)) {
        this.errorType = 'email';
      } else if (this.bzPattern === 'url' && this.bzValue && !this.urlRegExp.test(this.bzValue)) {
        this.errorType = 'url';
      } else if (this.bzPattern === 'number' && this.bzValue && !this.numberRegExp.test(this.bzValue)) {
        this.errorType = 'number';
      } else if (this.bzPattern === 'tel' && this.bzValue && !this.telRegExp.test(this.bzValue)) {
        this.errorType = 'tel';
      } else if (this.bzPattern === 'mobile' && this.bzValue && !this.mobileRegExp.test(this.bzValue)) {
        this.errorType = 'mobile';
      }
    }
  }

  setValue(value: any) {
    if (value === this.bzValue) {
      return;
    }
    this.touched = true;
    this.bzValueChange.emit(value);
  }

  setRangeValue(value: any, index: number) {
    if (!this.bzValue) {
      this.bzValue = [];
    }
    if (value) {
      if (this.bzValue[index] === value) {
        return;
      }
      this.bzValue[index] = value;
    } else {
      if (index === 1) {
        let _value = [this.bzValue[0]];
        [this.bzValue, _value] = [_value, this.bzValue];
      } else {
        this.bzValue = [];
      }
    }
    this.touched = true;
    this.bzValueChange.emit(this.bzValue);
  }
  getRangeValue(index) {
    let value = '';
    try {
      value = this.bzValue[index] || '';
    } catch (error) {
      value = '';
    }
    return value;
  }

  onFocus(...rest) {
    this.bzFocus.emit(...rest);
  }

  onBlur(...rest) {
    this.touched = true;
    this.bzBlur.emit(...rest);
  }

  onMousedown(...rest) {
    this.touched = true;
    this.bzMousedown.emit(...rest);
  }

  onOpenChange(e) {
    if (e === false) {
      this.touched = true;
    }

    this.bzConfig.onOpenChange(e);
  }

  isEmpty() {
    // 校验多选下拉框为空
    if ((this.bzType === 'select' || this.bzType === 'tree-select') && this.bzConfig && this.bzConfig.mode === 'multiple'
      && (!Array.isArray(this.bzValue) || this.bzValue.length === 0)) {
      return true;
    } else if (this.bzType === 'checkbox' && (!Array.isArray(this.bzValue) || this._bzConfig.bzValue.length === 0)) {
      return true;
    }
    return this.bzValue === null || this.bzValue === undefined || (typeof this.bzValue === 'string' && this.bzValue.trim().length === 0);
  }

  resetValue() {
    const defaultValue = this._getDefaultValue();
    if ((this.bzType === 'input-range' || this.bzType === 'checkbox') && !Array.isArray(defaultValue)) {
      this.setValue([]);
    } else {
      this.setValue(defaultValue);
    }
  }

  getCheckBoxValue(option) {
    const value = this.bzOptionValue === '$this' ? option : option[this.bzOptionValue];
    for (let i = 0; i < this.bzValue.length; i++) {
      const v = this.bzValue[i];
      if (this.bzCompareWith(value, v)) {
        return true;
      }
    }
    return false;
  }

  setCheckBoxValue(e, option) {
    const value = this.bzOptionValue === '$this' ? option : option[this.bzOptionValue];
    for (let i = 0; i < this.bzValue.length; i++) {
      const v = this.bzValue[i];
      if (this.bzCompareWith(value, v)) {
        this.bzValue.splice(i, 1);
      }
    }

    if (e === true) {
      this.bzValue.push(value);
    }
    this.touched = true;
    this.bzValueChange.emit([...this.bzValue]);
  }

  getShow() {
    if (this.bzRequired) {
      return true;
    } else if (this.bzShow !== undefined) {
      return this.bzShow;
    } else if (this.customShow !== undefined) {
      return this.customShow;
    } else if (this.defaultShow !== undefined) {
      return this.defaultShow;
    }
    return true;
  }

  private _compareWithFn(o1: any, o2: any) {
    if (isObject(o1) || isObject(o2)) {
      return o1 === o2;
    } else {
      // tslint:disable-next-line:triple-equals
      return o1 == o2;
    }
  }

  private _setDefaultValue(val) {
    if (isDate(val)) {
      this._defaultValue = '$d-' + val.getTime();
    } else if (Array.isArray(val) && (isDate(val[0]) || isDate(val[1]))) {
      this._defaultValue = '$r-' + JSON.stringify(val.map(v => v.getTime()));
    } else {
      this._defaultValue = this.bzValue !== null ? JSON.stringify(this.bzValue) : undefined;
    }
  }

  private _getDefaultValue() {
    if (this._defaultValue) {
      if (this._defaultValue.startsWith('$d-')) {
        return new Date(+this._defaultValue.slice(3));
      } else if (this._defaultValue.startsWith('$r-')) {
        return JSON.parse(this._defaultValue.slice(3)).map((v) => {
          return isNumber(v) && new Date(v);
        });
      } else {
        return JSON.parse(this._defaultValue);
      }
    } else {
      return undefined;
    }
  }
}
